#!/usr/bin/env python

import os
import sys
import getopt

# ROOT
#   - kfg
#       - # This file, used to generate the Makefile
#   - Makefile.defs
#   - Makefile.rules
#   - PLATFORMS/
#       - # GCC: x86, 7231, 7356
#       - CURRENT
#           - # LINK (link -s) to current PLATFORM
#       - x86
#       - 7231
#       - 7356
#   - BUILDS/
#       - CURRENT
#           - # LINK (link -s) to current BUILD
#       - BUILD_NAME_A
#       - BUILD_NAME_B
#   - SRCS/
#       - SRC_DIR_NAME_A/
#           - README.md
#           - Makefile
#           - xxx.c
#       - SRC_DIR_NAME_B/

if os.path.islink(__file__):
    os.chdir(os.path.dirname(os.readlink(__file__)))

PWD = os.getcwd()

DIR_PLATFORMS = "PLATFORMS"
DIR_BUILDS = "BUILDS"
DIR_SRCS = "SRCS"

FILE_CURRENT = "CURRENT"
FILE_MAKEFILE_DEFS = "Makefile.defs"
FILE_MAKEFILE_RULES = "Makefile.rules"

PLATFORM_CUR = None
PLATFORM_ALL = []
BUILD_CUR = None
BUILD_ALL = []

PATH_MAKEFILE = os.path.join(PWD, "Makefile")
PATH_PLATFORM_CUR = os.path.join(PWD, DIR_PLATFORMS, FILE_CURRENT)
PATH_BUILD_CUR = os.path.join(PWD, DIR_BUILDS, FILE_CURRENT)

SRCS = None

PATH_BUILD_ENV = os.path.join(PWD, ".buildenv")

def update_settings():
    ''' Return the FULL Path of each file. '''
    global PLATFORM_CUR
    PLATFORM_CUR = get_platform_current()

    global BUILD_CUR
    BUILD_CUR = get_build_current()

    global PLATFORM_ALL
    PLATFORM_ALL = get_platforms()

    global BUILD_ALL
    BUILD_ALL = get_builds()

    global SRCS
    try:
        path = os.path.join(PWD, DIR_BUILDS, BUILD_CUR)
        if os.path.exists(path):
            SRCS = get_srcs_from_build(path)
        else:
            SRCS = []
    except:
        SRCS = []

def follow_symlink(name):
    return os.path.basename(os.path.realpath(name))

def get_platforms():
    l = os.listdir(DIR_PLATFORMS)
    if FILE_CURRENT in l:
        l.remove(FILE_CURRENT)
    return l

def get_platform_current():
    path = os.path.join(PWD, DIR_PLATFORMS, FILE_CURRENT)
    if os.path.exists(path):
        return follow_symlink(path)
    else:
        return None

def get_builds():
    try:
        l = os.listdir(DIR_BUILDS)
        if FILE_CURRENT in l:
            l.remove(FILE_CURRENT)
        return l
    except:
        return []

def get_build_current():
    path = os.path.join(PWD, DIR_BUILDS, FILE_CURRENT)
    if os.path.exists(path):
        return follow_symlink(path)
    else:
        return None

def get_srcs_from_build(build_dir):
    try:
        current = get_build_current()
        path = os.path.join(PWD, DIR_BUILDS, current)
        return [ l.strip() for l in open(path, "rt").readlines()]
    except:
        return []
 
def load_Makefile_defs():
    return open(os.path.join(PWD, FILE_MAKEFILE_DEFS)).read()

def link_dot_buildenv():
    if os.path.exists(PATH_BUILD_ENV):
        os.remove(PATH_BUILD_ENV)
    os.symlink(PATH_PLATFORM_CUR, PATH_BUILD_ENV)

def output_makefile():
    buff = "# Note: Automatically generated by kfg, do not edit."
    buff += "\n\n"

    buff += "export BKM_PRJ_ROOT := $(CURDIR)\n"
    buff += "export BKM_PLATFORM := '%s'\n" % PLATFORM_CUR
    buff += "export BKM_BUILD := '%s'\n\n" % BUILD_CUR
    buff += "\n"

    buff += "-include Makefile.defs\n"

    buff += ".PHONY: all clean install uninstall info renew\n\n"

    for src in SRCS:
        buff += "#########################################################################\n"
        buff += "## %s\n" % src
        buff += "#\n"

        for pat in [ "all", "clean", "install", "uninstall"]:
            buff += ".PHONY: %s.%s\n" % (src, pat)
            buff += "%s: %s.%s\n" % (pat, src, pat)
            buff += "%s.%s:\n" % (src, pat)
            buff += "\tmake -C %s %s\n\n" % (os.path.join(PWD, DIR_SRCS, src), pat)

    buff += "#########################################################################\n"
    buff += "## %s\n" % "Information"
    buff += "#\n"
    buff += "info:\n"
    buff += "\t@echo '    ALL PLATFORMS : %s'\n" % " ".join(PLATFORM_ALL)
    buff += "\t@echo ' CURRENT PLATFORM : %s'\n" % PLATFORM_CUR
    buff += "\t@echo ''\n"
    buff += "\t@echo '       ALL BUILDS : %s'\n" % " ".join(BUILD_ALL)
    buff += "\t@echo '    CURRENT BUILD : %s'\n" % BUILD_CUR
    buff += "\t@echo '             SRCS : %s'\n" % " ".join(SRCS)

    buff += "#########################################################################\n"
    buff += "## %s\n" % "re-generate this Makefile"
    buff += "#\n"
    buff += "renew:\n"
    buff += "\t@python kfg -b '%s' -p '%s'\n" % (BUILD_CUR, PLATFORM_CUR)
    buff += "\t@python kfg -d"

    # print buff

    makefile = open(os.path.join(PWD, "Makefile"), "w")
    makefile.write(buff)
    makefile.close()

def dump_setting(platform = False, build = False):
    if platform:
        print "    ALL PLATFORMS : %s" % " ".join(PLATFORM_ALL)
        print " CURRENT PLATFORM : %s" % PLATFORM_CUR

    if platform and build:
        print ""

    if build:
        print "       ALL BUILDS : %s" % " ".join(BUILD_ALL)
        print "    CURRENT BUILD : %s" % BUILD_CUR
        print "             SRCS : %s" % " ".join(SRCS)

def help():
    print "usage: -h -d -p platform -b build"

def ensure_essential_dir():
    path = os.path.join(PWD, DIR_PLATFORMS)
    if not os.path.exists(path):
        os.mkdir(path)

    path = os.path.join(PWD, DIR_BUILDS)
    if not os.path.exists(path):
        os.mkdir(path)

    path = os.path.join(PWD, DIR_SRCS)
    if not os.path.exists(path):
        os.mkdir(path)

if __name__ == "__main__":
    ensure_essential_dir()
    update_settings()

    if len(sys.argv) == 1:
        help()
        print "----"
        dump_setting(True, True)
        sys.exit(0)

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hsdp:b:", [])
    except getopt.GetoptError as err:
        help()
        print "----"
        dump_setting(True, True)
        sys.exit(0)

    slience = False

    for o, a in opts:
        if o == '-h':
            help()
            sys.exit(0)

        elif o == '-s':
            slience = True

        elif o == '-d':
            slience = False

        elif o == '-p':
            if a in PLATFORM_ALL:
                if a != PLATFORM_CUR:
                    if os.path.exists(PATH_PLATFORM_CUR):
                        os.remove(PATH_PLATFORM_CUR)
                    os.symlink(a, PATH_PLATFORM_CUR)
                    update_settings()
            else:
                print "Platform (%s) not in PLATFORM_ALL %s" % (a, PLATFORM_ALL)
                sys.exit(0)

        elif o == '-b':
            if a in BUILD_ALL:
                if a != BUILD_CUR:
                    if os.path.exists(PATH_BUILD_CUR):
                        os.remove(PATH_BUILD_CUR)
                    os.symlink(a, PATH_BUILD_CUR)
                    update_settings()
            else:
                print "Build (%s) not in BUILD_ALL %s" % (a, BUILD_ALL)
                sys.exit(0)
        else:
            help()
            print "----"
            dump_setting(True, True)
            sys.exit(0)


    if PLATFORM_CUR and not PLATFORM_CUR in PLATFORM_ALL:
        print "PLATFORM_CUR (%s) not in PLATFORM_ALL %s" % (PLATFORM_CUR, PLATFORM_ALL)

    if BUILD_CUR and not BUILD_CUR in BUILD_ALL:
        print "BUILD_CUR (%s) not in BUILD_ALL %s" % (BUILD_CUR, BUILD_ALL)

    if PLATFORM_CUR and BUILD_ALL:
        link_dot_buildenv()
        output_makefile()

    if not slience:
        dump_setting(True, True)
        sys.exit(0)

